let THREE=require('three');

class CubemapGenerator{
    constructor(renderer){
        this.renderer = renderer;
    }
    fromEquirectangular( texture, options ) {

        options = options || {};

        let scene = new THREE.Scene();

        let shader = {

            uniforms: {
                tEquirect: { value: null },
            },

            vertexShader:

                `
			varying vec3 vWorldDirection;
			//include <common>
			vec3 transformDirection( in vec3 dir, in mat4 matrix ) {
				return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );
			}
			void main() {
				vWorldDirection = transformDirection( position, modelMatrix );
				#include <begin_vertex>
				#include <project_vertex>
			}
			`,

            fragmentShader:

                `
			uniform sampler2D tEquirect;
			varying vec3 vWorldDirection;
			//include <common>
			#define RECIPROCAL_PI 0.31830988618
			#define RECIPROCAL_PI2 0.15915494
			void main() {
				vec3 direction = normalize( vWorldDirection );
				vec2 sampleUV;
				sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;
				sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;
				gl_FragColor = texture2D( tEquirect, sampleUV );
			}
			`
        };

        let material = new THREE.ShaderMaterial( {

            type: 'CubemapFromEquirect',

            uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
            vertexShader: shader.vertexShader,
            fragmentShader: shader.fragmentShader,
            side: THREE.BackSide,
            blending: THREE.NoBlending

        } );

        material.uniforms.tEquirect.value = texture;

        let mesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 5, 5, 5 ), material );

        scene.add( mesh );

        let resolution = options.resolution || 512;

        let params = {
            type: texture.type,
            format: texture.format,
            encoding: texture.encoding,
            generateMipmaps: ( options.generateMipmaps !== undefined ) ? options.generateMipmaps : texture.generateMipmaps,
            minFilter: ( options.minFilter !== undefined ) ? options.minFilter : texture.minFilter,
            magFilter: ( options.magFilter !== undefined ) ? options.magFilter : texture.magFilter
        };

        let camera = new THREE.CubeCamera( 1, 10, resolution, params );

        camera.update( this.renderer, scene );

        mesh.geometry.dispose();
        mesh.material.dispose();

        return camera.renderTarget;

    }
}

class ETCGenerator{
    constructor( sourceTexture, options ) {
        this.camera = new THREE.PerspectiveCamera( 90, 1, 0.1, 10 );
        this.scene = new THREE.Scene();
        this.boxMesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 1, 1, 1 ), this.getShader() );
        this.boxMesh.material.side = THREE.BackSide;
        this.scene.add( this.boxMesh );

        options = options || {};

        this.sourceTexture = sourceTexture;
        this.resolution = options.resolution || 512;

        this.views = [
            { t: [ 1, 0, 0 ], u: [ 0, - 1, 0 ] },
            { t: [ - 1, 0, 0 ], u: [ 0, - 1, 0 ] },
            { t: [ 0, 1, 0 ], u: [ 0, 0, 1 ] },
            { t: [ 0, - 1, 0 ], u: [ 0, 0, - 1 ] },
            { t: [ 0, 0, 1 ], u: [ 0, - 1, 0 ] },
            { t: [ 0, 0, - 1 ], u: [ 0, - 1, 0 ] },
        ];

        let params = {
            format: options.format || this.sourceTexture.format,
            magFilter: this.sourceTexture.magFilter,
            minFilter: this.sourceTexture.minFilter,
            type: options.type || this.sourceTexture.type,
            generateMipmaps: this.sourceTexture.generateMipmaps,
            anisotropy: this.sourceTexture.anisotropy,
            encoding: this.sourceTexture.encoding
        };

            this.renderTarget = new THREE.WebGLRenderTargetCube(this.resolution, this.resolution, params);
    }
    getShader(){

    let shaderMaterial = new THREE.ShaderMaterial( {

        uniforms: {
            "equirectangularMap": { value: null },
        },

        vertexShader:
            "varying vec3 localPosition;\n\
            \n\
            void main() {\n\
              localPosition = position;\n\
              gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
            }",

        fragmentShader:
            "#include <common>\n\
            varying vec3 localPosition;\n\
            uniform sampler2D equirectangularMap;\n\
            \n\
            vec2 EquirectangularSampleUV(vec3 v) {\n\
              vec2 uv = vec2(atan(v.z, v.x), asin(v.y));\n\
              uv *= vec2(0.1591, 0.3183); // inverse atan\n\
              uv += 0.5;\n\
              return uv;\n\
            }\n\
            \n\
            void main() {\n\
              vec2 uv = EquirectangularSampleUV(normalize(localPosition));\n\
              gl_FragColor = texture2D(equirectangularMap, uv);\n\
            }",

        blending: THREE.NoBlending

    } );

    shaderMaterial.type = 'EquirectangularToCubeGenerator';

    return shaderMaterial;

}
    update( renderer ) {

    let currentRenderTarget = renderer.getRenderTarget();

    this.boxMesh.material.uniforms.equirectangularMap.value = this.sourceTexture;

    for ( let i = 0; i < 6; i ++ ) {

        let v = this.views[ i ];

        this.camera.position.set( 0, 0, 0 );
        this.camera.up.set( v.u[ 0 ], v.u[ 1 ], v.u[ 2 ] );
        this.camera.lookAt( v.t[ 0 ], v.t[ 1 ], v.t[ 2 ] );

        renderer.setRenderTarget( this.renderTarget, i );
        renderer.clear();
        renderer.render( this.scene, this.camera );

    }

    renderer.setRenderTarget( currentRenderTarget );
        return this.renderTarget.texture;
}
    dispose() {

    this.renderTarget.dispose();

}
}

export {
    CubemapGenerator,
    ETCGenerator,
};

